package fleet

import (
	"encoding/json"
	"fmt"
	"time"
)

type VPPAppID struct {
	// AdamID is a unique identifier assigned to each app in
	// the App Store, this value is managed by Apple.
	AdamID   string                    `db:"adam_id" json:"app_store_id"`
	Platform InstallableDevicePlatform `db:"platform" json:"platform"`
}

// VPPAppTeam contains extra metadata injected by fleet
type VPPAppTeam struct {
	VPPAppID

	AppTeamID   uint `db:"id" json:"-"`
	SelfService bool `db:"self_service" json:"self_service"`

	// InstallDuringSetup is either the stored value of that flag for the VPP app
	// or the value to set to that VPP app when batch-setting it. When used to
	// set the value, if nil it will keep the currently saved value (or default
	// to false), while if not nil, it will update the flag's value in the DB.
	InstallDuringSetup *bool `db:"install_during_setup" json:"-"`
	// LabelsIncludeAny are the names of labels associated with this app. If a host has any of
	// these labels, the app is in scope for that host. If this field is set, LabelsExcludeAny
	// cannot be set.
	LabelsIncludeAny []string `json:"labels_include_any"`
	// LabelsExcludeAny are the names of labels associated with this app. If a host has any of
	// these labels, the app is out of scope for that host. If this field is set, LabelsIncludeAny
	// cannot be set.
	LabelsExcludeAny []string `json:"labels_exclude_any"`
	// ValidatedLabels are the labels (either include or exclude any) that have been validated by
	// Fleet as being valid labels. This field is only used internally.
	ValidatedLabels *LabelIdentsWithScope `json:"-"`
	// AddAutoInstallPolicy indicates whether or not we should create an auto-install policy for
	// this VPP app on VPP app add to Fleet.
	AddAutoInstallPolicy bool `json:"-"`
	// AddedAt is when the VPP app was added to the team
	AddedAt     time.Time `db:"added_at" json:"created_at"`
	Categories  []string  `json:"categories"`
	CategoryIDs []uint    `json:"-"`
	// AddedAutomaticInstallPolicy is the auto-install policy that can be
	// automatically created when a VPP app is added to Fleet. This field should be set after VPP
	// app creation if AddAutoInstallPolicy is true.
	AddedAutomaticInstallPolicy *Policy `json:"-"`
	DisplayName                 *string `json:"display_name"`
	// Configuration is a json file used to customize Android app
	// behavior/settings. Applicable to Android apps only.
	Configuration json.RawMessage `json:"configuration,omitempty"`
}

// VPPApp represents a VPP (Volume Purchase Program) application,
// this is used by Apple MDM to manage applications via Apple
// Business Manager.
type VPPApp struct {
	VPPAppTeam
	// BundleIdentifier is the unique bundle identifier of the
	// Application.
	BundleIdentifier string `db:"bundle_identifier" json:"bundle_identifier"`
	// IconURL is the URL of this App icon
	IconURL string `db:"icon_url" json:"icon_url"`
	// Name is the user-facing name of this app.
	Name string `db:"name" json:"name"`
	// LatestVersion is the latest version of this app.
	LatestVersion string `db:"latest_version" json:"latest_version"`
	// TeamID is used for authorization, it must be json serialized to be available
	// to the rego script. We don't set it outside authorization anyway, so it
	// won't render otherwise.
	TeamID  *uint `db:"-" json:"team_id,omitempty"`
	TitleID uint  `db:"title_id" json:"-"`

	CreatedAt time.Time `db:"created_at" json:"-"`
	UpdatedAt time.Time `db:"updated_at" json:"-"`
}

// AuthzType implements authz.AuthzTyper.
func (v *VPPApp) AuthzType() string {
	return "installable_entity"
}

// VPPAppStoreApp contains the field required by the get software title
// endpoint to represent an App Store app (VPP app).
type VPPAppStoreApp struct {
	VPPAppID
	Name          string               `db:"name" json:"name"`
	LatestVersion string               `db:"latest_version" json:"latest_version"`
	IconURL       *string              `db:"icon_url" json:"-"`
	Status        *VPPAppStatusSummary `db:"-" json:"status"`
	SelfService   bool                 `db:"self_service" json:"self_service"`
	// only filled by GetVPPAppMetadataByTeamAndTitleID
	VPPAppsTeamsID uint `db:"vpp_apps_teams_id" json:"-"`
	// AutomaticInstallPolicies is the list of policies that trigger automatic
	// installation of this software.
	AutomaticInstallPolicies []AutomaticInstallPolicy `json:"automatic_install_policies" db:"-"`
	// LabelsIncludeAny is the list of "include any" labels for this app store app (if not nil).
	LabelsIncludeAny []SoftwareScopeLabel `json:"labels_include_any" db:"labels_include_any"`
	// LabelsExcludeAny is the list of "exclude any" labels for this app store app (if not nil).
	LabelsExcludeAny []SoftwareScopeLabel `json:"labels_exclude_any" db:"labels_exclude_any"`
	// BundleIdentifier is the bundle identifier for this app.
	BundleIdentifier string `json:"-" db:"bundle_identifier"`
	// AddedAt is when the VPP app was added to the team
	AddedAt time.Time `db:"added_at" json:"created_at"`
	// Categories is the list of categories to which this software belongs: e.g. "Productivity",
	// "Browsers", etc.
	Categories  []string `json:"categories"`
	DisplayName string   `json:"display_name"`
	// Configuration is a json file used to customize Android app
	// behavior/settings. Applicable to Android apps only.
	Configuration json.RawMessage `json:"configuration,omitempty"`
}

// VPPAppStatusSummary represents aggregated status metrics for a VPP app.
type VPPAppStatusSummary struct {
	// Installed is the number of hosts that have the VPP app installed.
	Installed uint `json:"installed" db:"installed"`
	// Pending is the number of hosts that have the VPP app pending installation.
	Pending uint `json:"pending" db:"pending"`
	// Failed is the number of hosts that have the VPP app installation failed.
	Failed uint `json:"failed" db:"failed"`
}

type ErrVPPTokenTeamConstraint struct {
	Name string
	ID   *uint
}

func (e ErrVPPTokenTeamConstraint) Error() string {
	return fmt.Sprintf("Error: %q team already has a VPP token. Each team can only have one VPP token.", e.Name)
}

// HostVPPSoftwareInstall represents a VPP software install attempt on a host.
type HostVPPSoftwareInstall struct {
	InstallCommandUUID   string     `db:"command_uuid"`
	InstallCommandAckAt  *time.Time `db:"ack_at"`
	HostID               uint       `db:"host_id"`
	InstallCommandStatus string     `db:"install_command_status"`
	BundleIdentifier     string     `db:"bundle_identifier"`
}

// HostAndroidVPPSoftwareInstall represents the payload needed to
// insert a VPP software install record for an Android host.
//
// NOTE: Currently only supported for setup experience, to revisit when
// we support Android app installs at-large (as it will then go through
// the upcoming queue). For this reason, user ID and (Fleet-) policy id
// are always null and self-service is always false, while platform is
// always android.
type HostAndroidVPPSoftwareInstall struct {
	HostID            uint   `db:"host_id"`
	AdamID            string `db:"adam_id"`             // for Android, this is the e.g. com.chrome application ID
	CommandUUID       string `db:"command_uuid"`        // uuid of the corresponding android_policy_request row
	AssociatedEventID string `db:"associated_event_id"` // for Android (for the current setup-experience-only approach), we overload this field to store the Android policy version ID
}

const (
	DefaultVPPInstallVerifyTimeout = 10 * time.Minute
	DefaultVPPVerifyRequestDelay   = 5 * time.Second
)

type AppStoreAppUpdatePayload struct {
	SelfService      *bool
	LabelsIncludeAny []string
	LabelsExcludeAny []string
	Categories       []string
	DisplayName      *string
	Configuration    json.RawMessage
}
